package com.green.common.util.utils;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * xml转换、解析工具类.
 * 
 * 
 * 
 */
public class XMLHelper {

	protected static final Logger LOGGER = LoggerFactory.getLogger(XMLHelper.class);

	/**
	 * 
	 * @param xmlData
	 * @return
	 */
	public static Document parseDocument(String xmlData) {
		Document doc = null;
		if ("".equalsIgnoreCase(xmlData) || "null".equalsIgnoreCase(xmlData)) {
			return null;
		}

		try {
			doc = DocumentHelper.parseText(xmlData);
			// System.out.println(doc.asXML());
		} catch (DocumentException e) {
			LOGGER.error("文本转化为document错误", e);
		}
		return doc;
	}

	/**
	 * 根据返回Map刷新Xml文档的xpath节点
	 * 
	 * @param docsrc
	 * @param parentPath
	 * @param resultMap
	 */
	@SuppressWarnings("rawtypes")
	public static void fillXmlDataFromMap(Document docsrc, String xPath, Map resultMap) {
		if (resultMap == null) {
			return;
		}

		// createXPath(docsrc, xPath);
		// Node nd = docsrc.selectSingleNode(xPath);
		// Element e = (Element) nd;

		Iterator it = resultMap.keySet().iterator();
		while (it.hasNext()) {
			Object obj = it.next();
			String str = "";
			if (resultMap.get(obj) != null) {
				str = String.valueOf(resultMap.get(obj));
			}
			String truePath = xPath + "/" + obj.toString().toLowerCase();
			createXPath(docsrc, truePath);
			Node nd = docsrc.selectSingleNode(truePath);
			Element e = (Element) nd;
			e.setText(str);
			// e.addElement(obj.toString().toLowerCase());

			// e.addAttribute(obj.toString().toLowerCase(), str);
		}

	}

	/**
	 * 刷新doc中对应节点
	 * 
	 * @param docsrc
	 * @param parentPath
	 * @param nodeXML
	 */
	public static void refreshXmlDataFromNode(Document docsrc, String parentPath, String nodeXML) {
		refreshXmlDataFromNode(docsrc, parentPath, nodeXML, false);
	}

	/**
	 * 刷新Xml对应节点
	 * 
	 * @param docsrc
	 * @param parentPath
	 * @param nodeXML
	 * @param directReplace
	 */
	public static void refreshXmlDataFromNode(Document docsrc, String parentPath, String nodeXML, boolean directReplace) {
		if ("".equalsIgnoreCase(nodeXML) || "null".equalsIgnoreCase(nodeXML)) {
			return;
		}
		// 加载节点XML
		Document doc = null;
		try {
			doc = DocumentHelper.parseText(nodeXML);
			// System.out.println(doc.asXML());
		} catch (DocumentException e) {
			LOGGER.error("", e);
		}
		Node nd = null;

		nd = doc.getRootElement();
		refreshXmlDataFromNode(docsrc, parentPath, nd, true);
	}

	/**
	 * TODO:请添加注释(修改好了把TODO删除)lqh add.
	 * 
	 * @param docsrc
	 * @param parentPath
	 * @param node
	 */
	public static void refreshXmlDataFromNode(Document docsrc, String parentPath, Node node) {
		refreshXmlDataFromNode(docsrc, parentPath, node, false);
	}

	/**
	 * 替换对应节点方法
	 * 
	 * @param docsrc
	 *            需刷新XML
	 * @param parentPath
	 *            路径
	 * @param nodeXML
	 *            需刷新进去的内容element
	 * @param directReplace是否直接采用替换
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void refreshXmlDataFromNode(Document docsrc, String xPath, Node node, boolean directReplace) {
		if (node == null) {
			return;
		}
		// 加载节点XML
		Node ndsrc = null;

		createXPath(docsrc, xPath);
		ndsrc = docsrc.selectSingleNode(xPath);
		String nodeName = xPath.substring(xPath.lastIndexOf("/") + 1);

		if (nodeName.indexOf("[") > 0) {
			nodeName = nodeName.substring(0, nodeName.indexOf("["));
		} else if (nodeName.indexOf("@") >= 0) {

		}
		node.setName(nodeName);
		if (directReplace) {
			// 直接替代XML
			List ele = ndsrc.getParent().content();
			ele.set(ele.indexOf(ndsrc), node);
		} else {

			// Element e = (Element) node;
			// // add by lwy 2008.04.29采用刷新方式，而不是替代方式
			// Element esrc = (Element) ndsrc;
			// for (int index = 0; index < e.attributeCount(); index++) {
			// Attribute attr = e.attribute(index);
			// esrc.addAttribute(attr.getName(), attr.getValue());
			// }
			//
			// for (int index = 0; index < e.elements().size(); index++) {
			// Element nodei = (Element) e.elements().get(index);
			//
			// if (esrc.element(nodei.getName()) != null) {
			// esrc.remove(esrc.element(nodei.getName()));// 如果添加的节点在原节点里存在，则首先清除节点
			// }
			// Element enew = esrc.addElement(nodei.getName());// 建立节点
			// if (nodei.elements() == null || nodei.elements().size() == 0) {// 如果没有子节点才更新新节点的文本
			// enew.setText(nodei.getText());
			// }
			// // esrc.addElement(nodei.getName(), nodei.getText());
			//
			// }
			refreshNode(docsrc, ndsrc, node);
			// refreshDoc(docsrc, xPath, ndsrc);
		}

	}

	/**
	 * 刷新当前Xml节点
	 * 
	 * @param ndsrc源节点
	 * @param node需刷新进去的节点
	 */
	public static void refreshNode(Document doc, Node ndsrc, Node node) {
		Element e = (Element) node;
		// add by lwy 2008.04.29采用刷新方式，而不是替代方式
		Element esrc = (Element) ndsrc;
		/**
		 * 更新属性
		 */

		for (int index = 0; index < e.attributeCount(); index++) {
			Attribute attr = e.attribute(index);
			esrc.addAttribute(attr.getName(), attr.getValue());
		}

		/**
		 * 更新节点值
		 */

		if (e.elements().size() > 0) {// 如果有子节点
			Element enew = null;
			List<Element> esrcList = esrc.elements();
			for (Element esrcc : esrcList) {
				esrc.remove(esrcc);
			}
			if (e.getName().equalsIgnoreCase(esrc.getName())) {
				enew = esrc;
			} else {
				enew = esrc.addElement(e.getName());
			}
			for (int i = 0; i < e.elements().size(); i++) {
				Element nodec = (Element) e.elements().get(i);// 取出需刷新进去的子节点
				Element enewc = enew.addElement(nodec.getName());// 生成源节点的子节点路径
				refreshNode(doc, enewc, nodec);// 递归调用进行替换
			}
		} else {
			if ((e.elements() == null || esrc.elements().size() == 0) && esrc.getName().equalsIgnoreCase(e.getName())) {// 如果添加的节点没有子节点,并且在原节点里存在，则首先清除节点
				esrc.setText(e.getText());
			}
			// Element enew = esrc.addElement(e.getName());// 建立节点
		}

	}

	public static void refreshDoc(Document doc, String xPath, Node node) {
		Node ndsrc = doc.selectSingleNode(xPath);
		ndsrc = node;
	}

	/**
	 * 根据xPath，创建节点.
	 * 
	 * @param docsrc
	 * @param xPath
	 *            ，形如：//data/persons/person[@submitpsn='1']/prp[@seq_no= 'ss']/test
	 */
	public static void createXPath(Document docsrc, String xPath) {

		xPath = xPath.replaceAll("//", "");
		String[] paths = xPath.split("/");
		String path = "/";
		String parent = null;
		Node nd = null;
		for (String path2 : paths) {
			path += "/" + path2;
			nd = docsrc.selectSingleNode(path);
			if (nd == null) {

				if (path2.indexOf("[@") != -1) {
					String nodeName = path2.substring(0, path2.indexOf("["));
					String attrName = path2.substring(path2.indexOf("@") + 1, path2.indexOf("="));
					String attrValue = path2.substring(path2.indexOf("=") + 2, path2.indexOf("]") - 1);

					Element el = (Element) docsrc.selectSingleNode(parent);
					el.addElement(nodeName);

					// 找到新增加的节点，并添加属性
					// el=(Element)docsrc.selectSingleNode(parent+"/"+nodeName);
					// el.addAttribute(attrName, attrValue);
					List<?> list = docsrc.selectNodes(parent + "/" + nodeName);
					for (int index = 0; index < list.size(); index++) {
						Element node = (Element) list.get(index);
						if (node.attributes().size() == 0) {
							node.addAttribute(attrName, attrValue);
							break;
						}
					}

					parent = path;
				} else if (path2.indexOf("@") != -1) {// 忽略属性

					continue;

				} else {
					Element el = (Element) docsrc.selectSingleNode(parent);
					el.addElement(path2);
					parent = path;
				}

			} else {
				parent = path;
			}
		}
	}

	/**
	 * 把一个表记录分解到xml节点中.
	 * 
	 * @param list
	 * @param parentNode
	 * @param subnode
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static String parseTableToXML(List list, String parentNode, String subnode) {
		if (list == null || list.size() == 0) {
			return "";
		}
		StringBuilder xml = new StringBuilder();

		if ("".equalsIgnoreCase(subnode)) {

			Map map = (Map) list.get(0);
			xml.append("<" + parentNode + " ");

			Iterator it = map.keySet().iterator();
			String key = null;
			String value = null;
			while (it.hasNext()) {
				key = it.next().toString().toLowerCase();
				value = StringUtils.filterNull(map.get(key));
				xml.append(pingXML(key, value));
			}
			xml.append("/>");
		} else {
			xml.append("<" + parentNode + ">");

			for (int i = 0; i < list.size(); i++) {
				xml.append("<" + subnode + " ");
				Map map = (Map) list.get(i);

				Iterator it = map.keySet().iterator();
				String key = null;
				String value = null;
				while (it.hasNext()) {
					key = it.next().toString().toLowerCase();
					value = StringUtils.filterNull(map.get(key));
					xml.append(pingXML(key, value));
				}
				xml.append("/>");
			}

			xml.append("</" + parentNode + ">");
		}
		return xml.toString();
	}

	/**
	 * 将xml拆分到数据库表中.
	 * 
	 * @param AllXML
	 * @param mergeSql
	 * @param parentPath
	 * @param colPath
	 * @param fields
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static List extractXMLToTable(String xml, String sql, String parentPath, String subPath, List fields) {
		List sqls = new ArrayList();

		Node nodes = getNodeFromXML(xml, parentPath);

		if (nodes != null) {
			for (Object nodeObj : nodes.selectNodes(subPath)) {
				List<Object> paramList = new ArrayList<Object>();

				Node node = (Node) nodeObj;
				if (node == null) {
					continue;
				}
				paramList.clear();

				for (int index = 0; index < fields.size(); index++) {
					Map map = (Map) fields.get(index);
					String type = String.valueOf(map.get("type"));
					String name = String.valueOf(map.get("name"));

					String value = node.valueOf(String.valueOf(name));

					if ("date".equalsIgnoreCase(type)) {
						SimpleDateFormat us = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
						Date date = null;
						try {
							if (value != null && !"".equals(value.trim())) {
								date = us.parse(value);
							}
						} catch (Exception e) {
							e.printStackTrace();
						}
						paramList.add(date);
					} else if ("long".equalsIgnoreCase(type)) {
						paramList.add(Long.parseLong(value));
					} else if ("decimal".equalsIgnoreCase(type)) {
						if ("".equalsIgnoreCase(value.trim())) {
							value = "0";
						} else {
							value = value.trim();
						}
						paramList.add(new BigDecimal(value));
					} else {
						paramList.add(value);
					}

				}

				// super.getJt().update(sql,paramList.toArray());
				HashMap sqlMap = new HashMap();
				sqlMap.put("sql", sql);
				sqlMap.put("params", paramList);
				sqls.add(sqlMap);
				// sqls.add(paramList);
			}
		}
		return sqls;

	}

	/**
	 * 从XML字符串中获取XML节点.
	 * 
	 * @param xml
	 * @param nodePath
	 * @return
	 */
	public static Node getNodeFromXML(String xml, String nodePath) {
		// 加载原XML
		Document doc = null;
		Node node = null;
		try {
			doc = DocumentHelper.parseText(xml);
			node = doc.selectSingleNode(nodePath);
		} catch (DocumentException e) {
			LOGGER.error("", e);
		}
		return node;
	}

	/**
	 * 从XML字符串中获取XML节点值.
	 * 
	 * @param xml
	 * @param nodePath
	 * @return
	 */
	public static String getNodeValueFromXML(String xml, String nodePath) {
		// 加载原XML
		Document doc = null;
		try {
			doc = DocumentHelper.parseText(xml);
		} catch (DocumentException e) {
			LOGGER.error("", e);
		}

		Node node = doc.selectSingleNode(nodePath);
		if (node == null) {
			return "";
		} else {
			return node.getText();
		}
	}

	/**
	 * 拼接xml属性.
	 * 
	 * @param key
	 *            节点属性名
	 * @param obj
	 *            属性值
	 * @return
	 */
	public static String pingXML(String key, Object obj) {
		String value = "";
		if (obj != null) {
			value = obj.toString();
		}

		value = Encoder.encodeHTML(value);
		return key + "=\"" + value + "\" ";
	}

	/**
	 * nodeSort(xmlDoc,"//data/persons","//data/persons/person","@sequence_no");.
	 * 
	 * @param xmlDoc
	 * @param parentXPath
	 * @param nodesXPath
	 * @param sortAttr
	 */
	@SuppressWarnings("rawtypes")
	public static void nodeSort(Document xmlDoc, String parentXPath, String nodesXPath, String sortAttr) {
		List nodes = xmlDoc.selectNodes(nodesXPath);
		Element p = (Element) xmlDoc.selectSingleNode(parentXPath);
		if (nodes.size() > 1) {
			Node[] result = nodeSortByAttritue(nodes, sortAttr);
			for (int i = 0; i < nodes.size(); i++) {
				((Node) nodes.get(i)).detach();
			}
			for (Node element : result) {
				p.add(element);
			}
		}
	}

	/**
	 * 调用nodeSortByAttritue(node,"@sequence_no").
	 * 
	 * @param nodes
	 * @param sortAttr
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	public static Node[] nodeSortByAttritue(List nodes, String sortAttr) {
		Node[] result = new Node[nodes.size()];
		/*
		 * for(int i=0;i<nodes.size();i++) { Node temp = (Node)nodes.get(i); String str = temp.valueOf(sortAttr);
		 * if(str!=null&&!"".equals(str)) { int currentSeqNo = Integer.parseInt(str); result[currentSeqNo-1] = temp; } }
		 */
		for (int i = 0; i < nodes.size(); i++) {
			result[i] = (Node) nodes.get(i);
		}

		Node temp;
		for (int i = 0; i < result.length; i++) {
			for (int j = result.length - 1; j > i; j--) {

				// result[j].valueOf(sortAttr).compareTo(result[j-1].valueOf(sortAttr))<0
				try {
					if (Integer.valueOf(result[j].valueOf(sortAttr)) < Integer.valueOf(result[j - 1].valueOf(sortAttr))) {
						// SortUtil.swap(data,j,j-1);
						temp = result[j];
						result[j] = result[j - 1];
						result[j - 1] = temp;
					}
					// 不能转化为数据进行比较的时候采用字符串比较
				} catch (Exception e) {
					if (result[j].valueOf(sortAttr).compareTo(result[j - 1].valueOf(sortAttr)) < 0) {
						// SortUtil.swap(data,j,j-1);
						temp = result[j];
						result[j] = result[j - 1];
						result[j - 1] = temp;
					}
				}

			}
		}

		return result;
	}

	/**
	 * document with w3c form to dom4j.
	 * 
	 * @param doc
	 * @return
	 */
	public static Document parseDoc(org.w3c.dom.Document doc) {
		if (doc == null) {
			return null;
		}
		org.dom4j.io.DOMReader xmlReader = new org.dom4j.io.DOMReader();
		return xmlReader.read(doc);
	}

	/**
	 * document with dom4j form to w3c.
	 * 
	 * @param doc
	 * @return
	 */
	public static org.w3c.dom.Document parseDoc(Document doc) throws Exception {
		if (doc == null) {
			return null;
		}
		java.io.StringReader reader = new java.io.StringReader(doc.asXML());
		org.xml.sax.InputSource source = new org.xml.sax.InputSource(reader);
		javax.xml.parsers.DocumentBuilderFactory documentBuilderFactory = javax.xml.parsers.DocumentBuilderFactory
				.newInstance();
		javax.xml.parsers.DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
		return documentBuilder.parse(source);
	}

	/**
	 * transform xml-string to document in form of w3c.
	 * 
	 * @param xmlstr
	 * @return
	 */
	public static org.w3c.dom.Document parseW3cDoc(String xmlstr) {
		try {
			return parseDoc(parseDocument(xmlstr));
		} catch (Exception e) {
			LOGGER.error("error occuring in transforming from xml string to w3c document", e);
			return null;
		}
	}

	public static org.w3c.dom.Node selectSingleNode(String express, Object source) {// 查找节点，并返回第一个符合条件节点
		org.w3c.dom.Node result = null;
		XPathFactory xpathFactory = XPathFactory.newInstance();
		XPath xpath = xpathFactory.newXPath();
		try {
			result = (org.w3c.dom.Node) xpath.evaluate(express, source, XPathConstants.NODE);
		} catch (XPathExpressionException e) {
			e.printStackTrace();
		}

		return result;
	}

	/**
	 * 负责对xml的并集合并更新 ，目前不涉及属性的更新.
	 * 
	 * @author lineshow 2011-8-30
	 * @param destDoc
	 *            目标XML DOM
	 * @param srcDoc
	 *            源XML DOM
	 */
	public static void mergeXml(Document destDoc, Document srcDoc) {
		if (destDoc == null) {
			destDoc = srcDoc;
			return;
		}
		Element srcRootElement = srcDoc.getRootElement();
		Element destRootElement = destDoc.getRootElement();
		String srcRootName = srcRootElement.getQName().getName();
		String destRootName = destRootElement.getQName().getName();
		if (!srcRootName.equals(destRootName)) {
			LOGGER.error("根路径不同，退出合并。");
			return;
		}
		mergeCoreFunc(destDoc, srcRootElement, "/");
	}

	/**
	 * 负责对xml的并集合并更新 ，目前不涉及属性的更新.（只适合评议人信息修改）
	 * 
	 * @author lineshow 2011-8-30
	 * @param destDoc
	 *            目标XML DOM
	 * @param srcDoc
	 *            源XML DOM
	 */
	public static void mergeXmlPsn(Document destDoc, Document srcDoc) {
		if (destDoc == null) {
			destDoc = srcDoc;
			return;
		}
		Element srcRootElement = srcDoc.getRootElement();
		Element destRootElement = destDoc.getRootElement();
		String srcRootName = srcRootElement.getQName().getName();
		String destRootName = destRootElement.getQName().getName();
		if (!srcRootName.equals(destRootName)) {
			LOGGER.error("根路径不同，退出合并。");
			return;
		}
		mergeCoreFuncPsn(destDoc, srcRootElement, "/");
	}

	/**
	 * TODO:请添加注释(修改好了把TODO删除)lqh add.
	 * 
	 * @param destDoc
	 * @param srcElement
	 * @param path
	 */
	@SuppressWarnings("unchecked")
	private static void mergeCoreFunc(Document destDoc, Element srcElement, String path) {
		String currentNodePath = path + "/" + srcElement.getQName().getName();
		Node node = destDoc.selectSingleNode(currentNodePath);
		if (node == null) {
			Element destE = (Element) destDoc.selectSingleNode(path);
			if (destE.elements().isEmpty()) {
				destE.clearContent();
			}
			destE.addElement(srcElement.getQName().getName());
			node = destDoc.selectSingleNode(currentNodePath);
		}
		if (srcElement.elements().isEmpty()) {
			node.setText(srcElement.getText());
			return;
		}
		Iterator<Element> elementIt = srcElement.elementIterator();
		while (elementIt.hasNext()) {
			Element cellElement = elementIt.next();
			mergeCoreFunc(destDoc, cellElement, currentNodePath);
		}
	}

	/**
	 * TODO:请添加注释(修改好了把TODO删除)lqh add.
	 * 
	 * @param destDoc
	 * @param srcElement
	 * @param path
	 */
	@SuppressWarnings("unchecked")
	private static void mergeCoreFuncPsn(Document destDoc, Element srcElement, String path) {
		String currentNodePath = path + "/" + srcElement.getQName().getName();
		String attrPath = "";
		if (!"//data".equals(currentNodePath)) {
			Iterator<Attribute> attributes = srcElement.attributeIterator();
			while (attributes.hasNext()) {
				Attribute attr = attributes.next();
				attrPath = "[@" + attr.getName() + "=\"" + attr.getText() + "\"]";
			}
		}
		currentNodePath = currentNodePath + attrPath;
		Node node = destDoc.selectSingleNode(currentNodePath);
		if (node == null) {
			createXPath(destDoc, currentNodePath);
			Element destE = (Element) destDoc.selectSingleNode(path);

			if (destE.elements().isEmpty()) {
				destE.clearContent();
			}
			node = destDoc.selectSingleNode(currentNodePath);
		}
		if (srcElement.elements().isEmpty()) {
			node.setText(srcElement.getText());
			return;
		}
		Iterator<Element> elementIt = srcElement.elementIterator();
		while (elementIt.hasNext()) {
			Element cellElement = elementIt.next();
			mergeCoreFuncPsn(destDoc, cellElement, currentNodePath);
		}
	}

	/**
	 * 测试入口.
	 * 
	 * @param args
	 * @throws UnsupportedEncodingException
	 * @throws DocumentException
	 */
	@SuppressWarnings("unused")
	public static void main(String[] args) throws Exception {
		String xmlstr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><data><budget><org_1_name>123123123</org_1_name><org_1_code>4312313</org_1_code></budget></data>";
		Document doc = XMLHelper.parseDocument(xmlstr);
		org.w3c.dom.Document w3cdom = XMLHelper.parseDoc(doc);
		Node node = doc.selectSingleNode("/data/aa");
		String value = node.getText();

	}

	/**
	 * 替换xml节点的值.
	 * 
	 * @param xmlData
	 * @param xmlPath
	 * @param value
	 * @return
	 */
	public static String refreshNodeValue(String xmlData, String xmlPath, Object value) {
		if (StringUtils.isNullOrBlank(xmlData)) {
			return xmlData;
		}
		String strValue = null;
		if (value == null) {// 去除value为null时toString方法报错情况
			strValue = "";
		} else {
			strValue = value.toString();
		}
		Document doc = null;
		try {
			doc = DocumentHelper.parseText(xmlData);
		} catch (Exception e) {
			LOGGER.error("doc转换错误", e);
		}
		if (doc == null) {
			return xmlData;
		}
		Element e = (Element) doc.selectSingleNode(xmlPath);
		if (e == null) {// 如果不存在节点就造出来
			createXPath(doc, xmlPath);
			e = (Element) doc.selectSingleNode(xmlPath);
		}
		e.setText(strValue);
		return doc.asXML();
	}
}